using System;
using System.Collections.Generic;
using System.Text;


namespace Noein.GeneticAlgorithm
{
    /// <summary>
    /// Encapsulate the solution the genetic algorithm is trying to find.
    /// </summary>
    /// <typeparam name="TGenotype">the genotype to be evolved</typeparam>
    /// <typeparam name="TPhenotype">the expression of the genotype</typeparam>
    public abstract class Individual<TGenotype, TPhenotype>
    {
        /// <summary>
        /// Format: generation number - individual number - parent 0 number - ... - parent n number
        /// </summary>
        public struct Identity
        {
            public int generationNum;
            public int individualNum;
            public int[] parentsGeneratinoNum;
            public int[] parentsIndividualNum;
        }

        private Identity id;
        protected TGenotype genotype;
        protected TPhenotype phenotype;
        //I, Juan, have set a default fitness to help realize when fitness has not been set.
        protected float fitness = -1;

        #region Properties
        public Identity Id
        {
            get { return id; }
            set { id = value; }
        }

        public TGenotype Genotype
        {
            get { return genotype; }
            //set { genotype = value; }
        }

        public TPhenotype Phenotype
        {
            get { return phenotype; }
            //set { phenotype = value; }
        }

        public float Fitness
        {
            get { return fitness; }
            set { fitness = value; }
        }
        #endregion

        /// <summary>
        /// Construct a individual with given gene.
        /// </summary>
        /// <param name="genotype">gene</param>
        public Individual(TGenotype genotype, params Individual<TGenotype, TPhenotype>[] parents)
        {
            // create individual id from parents' ids
            id = new Identity();
            id.parentsGeneratinoNum = new int[parents.Length];
            id.parentsIndividualNum = new int[parents.Length];
            for (int i = 0; i < parents.Length; i++)
            {
                id.parentsGeneratinoNum[i] = parents[i].Id.generationNum;
                id.parentsIndividualNum[i] = parents[i].id.individualNum;
            }
            
            this.genotype = genotype;
            phenotype = GeneticMapping(genotype);
        }

        /// <summary>
        /// Called by population when this individual gets added to the population.
        /// </summary>
        /// <param name="generationNum">generation this individual belongs to</param>
        /// <param name="individualNum">uniqually identity this individual within its generation</param>
        public void UpdateId(int generationNum, int individualNum)
        {
            id.generationNum = generationNum;
            id.individualNum = individualNum;
        }

        /// <summary>
        /// Maps genotype to phenotype.
        /// </summary>
        /// <param name="genotype">gene</param>
        /// <returns></returns>
        public abstract TPhenotype GeneticMapping(TGenotype genotype);

        /// <summary>
        /// For debug print.
        /// </summary>
        /// <returns>information relating to this individual</returns>
        public override String ToString()
        {
            String retval = "ID: ";

            retval += id.generationNum + "-" + id.individualNum;

            for (int i = 0; i < id.parentsGeneratinoNum.Length; i++)
            {
                retval += "-" + id.parentsGeneratinoNum[i] + "-" + id.parentsIndividualNum[i];
            }

            retval += " \tfitness: " + fitness;

            return retval;
        }


        //Juan Rodriguez:
        /// <summary>
        /// Return this individual's fitness.
        /// </summary>
        /// <returns></returns>
        public float getFitness()
        {
            return fitness;
        }
        /// <summary>
        /// Surely we would set fitness before getting it.  This is done in the fitness function.
        /// </summary>
        /// <param name="f"></param>
        public void setFitness(float f)
        {
            fitness = f;
        }


    }
}
